package cn.abcode.test.springbootsecurity.demo.config;

import cn.abcode.test.springbootsecurity.demo.auth.AuthenticationCustomProvider;
import cn.abcode.test.springbootsecurity.demo.auth.CustomAuthenticationDetailsSource;
import cn.abcode.test.springbootsecurity.demo.auth.CustomWebAuthenticationDetails;
import cn.abcode.test.springbootsecurity.demo.filter.VerifyCodeFilter;
import cn.abcode.test.springbootsecurity.demo.handler.AccessDeniedCustomHandler;
import cn.abcode.test.springbootsecurity.demo.handler.AuthenticationFailCustomHandler;
import cn.abcode.test.springbootsecurity.demo.jpa.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.util.Arrays;

//@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService userService;
    @Autowired
    VerifyCodeFilter verifyCodeFilter;
    @Autowired
    AccessDeniedCustomHandler accessDeniedCustomHandler;
    @Autowired
    AuthenticationFailCustomHandler authenticationFailCustomHandler;

    @Autowired
    CustomAuthenticationDetailsSource customAuthenticationDetailsSource;

    @Bean
    PasswordEncoder passwordEncoder(){
        //return new  BCryptPasswordEncoder (10);
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    AuthenticationCustomProvider authenticationCustomProvider(){
        AuthenticationCustomProvider provider = new AuthenticationCustomProvider();
        provider.setPasswordEncoder(passwordEncoder());
        provider.setUserDetailsService(userService);
        return provider;
    }

    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        ProviderManager manager = new ProviderManager(Arrays.asList(authenticationCustomProvider()));
        return manager;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userService);
       /* auth.inMemoryAuthentication()
                .withUser("zhou")
                .password("123456")
                .roles("admin")
                .and()
                .withUser("guo")
                .password("123456")
                .roles("user");*/
    }

/*    @Autowired
    DataSource dataSource;

    @Override
    @Bean
    protected UserDetailsService userDetailsService(){
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
        manager.setDataSource(dataSource);
        if(!manager.userExists("zhou")){
            manager.createUser(User.withUsername("zhou").password("123456").roles("admin").build());
        }
        if(!manager.userExists("guo")){
            manager.createUser(User.withUsername("guo").password("123456").roles("user").build());
        }
        return manager;
    }*/

    @Override
    public void configure(WebSecurity web) throws Exception {
        //忽略静态文件
        web.ignoring().antMatchers("/js/**", "/css/**","/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        //http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .antMatchers("/kaptcha/getCode").permitAll()
                .anyRequest().authenticated()
                .and()
                //登录配置
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/doLogin") //登录请求
                .usernameParameter("account")   //用户对应的字段
                .passwordParameter("pwd")       //密码对应的字段
                .authenticationDetailsSource(customAuthenticationDetailsSource)
                //成功后的跳转defaultSuccessUrl和successForwardUrl只需配置一个
                //.defaultSuccessUrl("/test/index")             //登录成功后跳转到的url,如果登录之前的请求路径是/test/hello，则登录成功后
                //.successForwardUrl("/test/index")       //登录成功后跳转到的url,不管登录之前的请求路径是什么，一定会跳转到/test/index
                //返回json
                .successHandler((request, response, authentication)->{
                    Object principal = authentication.getPrincipal();
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write(new ObjectMapper().writeValueAsString(principal));
                    out.flush();
                    out.close();
                })

                //登录失败跳转
                //.failureUrl("/test/fail")
                //.failureForwardUrl("/test/fail")
                .failureHandler((request, response, e)->{
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();

                   String msg = e.getMessage();
                    if (e instanceof LockedException) {
                        msg = "账户被锁定，请联系管理员!";
                    } else if (e instanceof CredentialsExpiredException) {
                        msg = "密码过期，请联系管理员!";
                    } else if (e instanceof AccountExpiredException) {
                        msg = "账户过期，请联系管理员!";
                    } else if (e instanceof DisabledException) {
                        msg = "账户被禁用，请联系管理员!";
                    } else if (e instanceof BadCredentialsException) {
                        msg = "用户名或者密码输入错误，请重新输入!";
                    }
                    out.write(msg);
                    out.flush();
                    out.close();
                })
                .permitAll()

                .and()
                //注销登录
                .logout()
                //注销请求url logoutUrl和 logoutRequestMatcher只需配置一个
                .logoutUrl("/test/logout")  //get请求
                //.logoutRequestMatcher(new AntPathRequestMatcher("/test/logout", "post"))
                //.logoutSuccessUrl("/test/outSuccess")
                .logoutSuccessHandler((request, response, authentication)->{
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write("注销成功");
                    out.flush();
                    out.close();
                })
                .deleteCookies()   //清除cookie
                .clearAuthentication(true)      //清除认证信息(默认值为true)
                .invalidateHttpSession(true)    //设置HttpSession失效(默认值为true)
                .permitAll()
                .and()
                .csrf().disable().exceptionHandling()
                //未认证处理
                .authenticationEntryPoint(authenticationFailCustomHandler)
                //无权限处理 可自定义类需实现AccessDeniedHandler
                .accessDeniedHandler(accessDeniedCustomHandler)

                ;
    }
}
